#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <arpa/inet.h>
#include <stdbool.h>
#include <mysql/mysql.h>
#include <sqlite3.h>
#include <stdint.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <semaphore.h>
#include <sys/epoll.h>
#include <openssl/sha.h>
#include <modbus/modbus.h>

/***********************************************************************
Copyright © Charles.A 2024. All rights reserved.
文件名 : server.c
作者 : 吴希文
版本 : V2.2
描述 : socket通信server端(Linux)
其他 : 无
日志 : 初版 V1.0 2024/9/16 吴希文创建
日志 : 更新 V1.1 2024/9/17 添加SSL信道加密
日志 : 更新 V1.2 2024/9/23 更新客户端ssl版本至3.3.2
日志 : 更新 V1.3 2024/9/24 新增注册功能
日志 : 更新 V1.4 2024/9/24 添加哈希算法以提高用户查找效率
日志 : 更新 V2.0 2024/9/25 对接了sqlite数据库
日志 : 更新 V2.1 2024/9/25 优化了用户交互提示
日志 : 更新 V2.2 2024/9/26 修正了SSL证书和密钥的加载方式,跨平台客户端可用
在项目内旦尚未开发 : 数据库信息加密,线程池,群体通信
debug:          2024/10/18 probably shared memo invalied use cause a segmentation fault.
debug:          2024/10/19 operate global structrue in a func cause segmentation fault? I 'd better not create thread in other func.(gpt said it is caused by complier optimize.)
*************************************************************************/

#define SERVER_PORT 12345
#define MAX_THREADS_NUM 10
#define HASHTABLE_SIZE 100
#define SHM_SIZE 1024
#define MAX_EVENTS 1024
#define MODBUS_SERVER_PORT 9999
#define MODBUS_SERVER_IP "0.0.0.0"
 
char sendbuf[1024] = {0};

sqlite3 *db;
sqlite3_stmt *res;

SSL_CTX *ctx;

int rc = 0;

sem_t message_sem;


pthread_mutex_t mutex;
pthread_cond_t cond;

int shared_memory_id;
char* shared_memory_ptr;

typedef struct {
	int user_id;
	char user_IP[128];
	char message[128];
	SSL *ssl;
	long target_id;
}User_info;

struct User{
	char username[256];
	char password[256];
};

typedef enum {
    SSL_MODE_SERVER,
    SSL_MODE_CLIENT
} SSL_MODE;

typedef struct {
	void *ssl;
	void *ptr_client_addr;
	sem_t semaphore;
	int client_socket;
	int signal_pipe[2];
}Thread_params;

struct User userTable[HASHTABLE_SIZE];

int online_client = 0;
int client_code = 0;

/*
 * @description     : SHA_256 encrypt method
 * @param -info     : input info
 * @param -info     : output encrypted info
 * @return          : void param as return
 */
void sha256(const unsigned char *info, unsigned char *hashed){
	SHA256_CTX ctx;
	SHA256_Init(&ctx);
	SHA256_Update(&ctx, info, strlen(info));
	SHA256_Final(hashed, &ctx);
}

/*
 * @description     : basic hash function 
 * @param -username : username
 * @return          : location in hash table  
 */
int hash_func(const char *username){
	int hash = 0;
	for(int i = 0; i < strlen(username); i++){
		hash += username[i]; 
	}
	return hash % HASHTABLE_SIZE;

}

/*
 * @description      : insert username and password to hashtable
 * @param - username : username
 * @param - password : password
 * @return           : if successed 
 */
int8_t insert_user(const char *username, const char *password){
	int step_counter = 0;
	int index = hash_func(username);
	if(userTable[index].username[0] == '\0'){
	    strcpy(userTable[index].username, username);
	    strcpy(userTable[index].password, password);
	    //printf("user %s added.\nhash index is:%d\n",userTable[index].username, index);
	    return 1;
	}
	while(strcmp(userTable[index].username, username) != 0){
		index = (index + 1) % HASHTABLE_SIZE;
		step_counter++;
		if(userTable[index].username[0] == '\0'){
	    		strcpy(userTable[index].username, username);
	    		strcpy(userTable[index].password, password);
	    		//printf("user %s added.\nhash index is:%d\n",userTable[index].username, index);
	    		return 1;
	    	}
		if(step_counter > 100){
			printf("user num out of max volume.");
			return -1;
		}
	}
}

/*
 * @description      : find username in hashtable to get it's password
 * @param - username : username 
 * @return           : target's password 
 */
char *find_user(char *username){
	int index = hash_func(username);
	if(userTable[index].username[0] == '\0'){
		//printf("user is not exsit.\n");
		return NULL;
	}
	while(strcmp(userTable[index].username, username) != 0){
		index = (index + 1) % HASHTABLE_SIZE;
		if(userTable[index].username[0] == '\0'){
			//printf("user is not exsit.\n");
			return NULL;
		}
	}
	//printf("user %s's password is %s.\nhash index is:%d\n",userTable[index].username, userTable[index].password, index);
	return userTable[index].password;
	
}

/*
 * @description   	    : check user's name and pass
 * @param - input_username : user's input name 
 * @param - input_password : user's input password
 * @return        	    : if username or password exsits
 */
int8_t is_user_permited(char *input_username, char *input_password){
	char *password = NULL;
	password = find_user(input_username);
	if(password == NULL) {
		return 1;
	}
	else if(strcmp(password, input_password) != 0){
		/*printf("in premit,password is:%s====\n",password);
		printf("in premit,input_password is:%s====\n",input_password);
		printf("password incorrect.\n");*/
		return 2;
	}
	else if(password != NULL && strcmp(password, input_password) == 0){
		/*printf("password correct.\n");*/
		return 0;
	}
	return 3;
}

/*
 * @description   	       : create and initialize ssl, include set key and cert, if error then output error info
 * @param - client_socket  : the fd witch will bind to ssl
 * @return        	       : ssl that usable
 */
SSL* sync_initialize_ssl(int client_socket){

    const SSL_METHOD *method;    
    SSL *ssl = NULL;
    SSL_MODE mode = SSL_MODE_SERVER;
    
    SSL_library_init();
    OpenSSL_add_all_algorithms();
    SSL_load_error_strings();								//initialize SSL algorithm lib (necessary).
    if (mode == SSL_MODE_SERVER) {
        method = SSLv23_server_method();
    } else if (mode == SSL_MODE_CLIENT) {
        method = SSLv23_client_method();
    } else {
        /* unknow method */
        printf("Not found method");
        return NULL;
    }
    
    ctx = SSL_CTX_new(method);
    if (!ctx) {
        printf("Unable to create SSL context");
        return NULL;
    }

    // disposition SSL context
    if (SSL_CTX_use_certificate_file(ctx, "cert.pem", SSL_FILETYPE_PEM) <= 0 || SSL_CTX_use_PrivateKey_file(ctx, "key.pem", SSL_FILETYPE_PEM) <= 0 ) {
        printf("Not found certificate or private key");
        SSL_CTX_free(ctx);
        return NULL;
    }
    // SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
    
    //SSL_CTX_set_ecdh_auto(ctx, 1);						 	//auto choosing ECDH(math curve) argument to enhance security of TLS(SSL plus) connection.
    //SSL_CTX_use_certificate_file(ctx, "server.crt", SSL_FILETYPE_PEM);		//set SSL certification(server).
    //SSL_CTX_use_PrivateKey_file(ctx, "server.key", SSL_FILETYPE_PEM);		//set SSL pravite Key(server).
    
    ssl = SSL_new(ctx);
    SSL_set_fd(ssl, client_socket);							//set socket handle for SSL* structure.
	
        								
	if ((mode == SSL_MODE_CLIENT && SSL_connect(ssl) <= 0) ||
        (mode == SSL_MODE_SERVER && SSL_accept(ssl) <= 0)) {
        int ssl_result;
        if (mode == SSL_MODE_CLIENT) {
            ssl_result = SSL_connect(ssl);
        } else if (mode == SSL_MODE_SERVER) {
            ssl_result = SSL_accept(ssl);
        }

        int ssl_err = SSL_get_error(ssl, ssl_result);
	
		/* error check */
		const char *err_str;
while(ssl_result != 1){        
        switch (ssl_err) {
            case SSL_ERROR_NONE:
                err_str = "No error";
                break;
            case SSL_ERROR_SSL:
                err_str = "Error in the SSL protocol";
                break;
            case SSL_ERROR_WANT_READ:
                err_str = "SSL read operation did not complete";
                continue;
            case SSL_ERROR_WANT_WRITE:
                err_str = "SSL write operation did not complete";
                break;
            case SSL_ERROR_WANT_X509_LOOKUP:
                err_str = "SSL X509 lookup operation did not complete";
                break;
            case SSL_ERROR_SYSCALL:
                err_str = "Syscall error";
                break;
            case SSL_ERROR_ZERO_RETURN:
                err_str = "SSL connection was shut down cleanly";
                break;
            case SSL_ERROR_WANT_CONNECT:
                err_str = "SSL connect operation did not complete";
                break;
            case SSL_ERROR_WANT_ACCEPT:
                err_str = "SSL accept operation did not complete";
                break;
            default:
                err_str = "Unknown error";
                break;
        }
    }
        printf("===============SSL handshake failed. Error: %s========!\n", err_str ? err_str : "Unknown");
        return NULL;
    }
    return ssl;
}

void *modbus_server(){
	
    /*modbus_t *ctx = NULL;
    modbus_mapping_t *mb_mapping = NULL;
    int rc = 0;*/
    
    // Create a new modbus TCP context
    modbus_t *ctx = modbus_new_tcp(MODBUS_SERVER_IP, MODBUS_SERVER_PORT);
    if (ctx == NULL) {
        fprintf(stderr, "Unable to create the Modbus TCP context\n");
        exit(1);
    }
    
    
    // Start Modbus server in listen mode
    rc = modbus_tcp_listen(ctx, 5);
    if (rc == -1) {
        fprintf(stderr, "Modbus listen failed: %s\n", modbus_strerror(errno));
        modbus_free(ctx);
        exit(1);
    }

    printf("start accept incoming Modbus request.\n");
    // Accept incoming Modbus connection
    modbus_tcp_accept(ctx, &rc);

    
    
    // Allocate space for the modbus mapping
    modbus_mapping_t *mb_mapping = modbus_mapping_new(100, 100, 200, 100);
    if (mb_mapping == NULL) {
        fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno));
        modbus_free(ctx);
        exit(1);
    }

    

    while (1) {
    
    // Set initial values for temperature and humidity registers
    mb_mapping->tab_registers[0] = (random() % 20)/10.0 + 20;  // Initial temperature value
    mb_mapping->tab_registers[1] = (random() % 30)/10.0 + 50;  // Initial humidity value
    mb_mapping->tab_registers[2] = (random() % 40)/10.0 + 20;  // Initial temperature value
    mb_mapping->tab_registers[3] = (random() % 50)/10.0 + 50;  // Initial humidity value

        // Handle incoming Modbus requests
        uint8_t query[4096];
        memset(query, 0, sizeof(query));
        rc = modbus_receive(ctx, query);
        if (rc > 0) {
        
            modbus_reply(ctx, query, rc, mb_mapping);
        } else if (rc == -1) {
            fprintf(stderr, "Modbus receive error: %s\n", modbus_strerror(errno));
            exit(1);
        }
        
        
    }

    // Free resources and close Modbus server
    modbus_mapping_free(mb_mapping);
    modbus_close(ctx);
    modbus_free(ctx);

    pthread_exit(NULL);
}

/*
 * @description   	    : core code, thread created when new client try to connet, check client's info and recive info that user sent
 * @param - packed_param   : struct ptr, include ssl and client addr, why pack? due to param restrict,the callback function in pthread_create function only allow 1 param follow and it must be typed with (void *)
 * @return        	    : actually no significant return, but pthread create indicate that callback fun should be void* type 
 */
void *handle_client(void *packed_param) {					//pack to pass the param of the function (due to param restrict,the callback function in pthread_create function only allow 1 param follow and it must be type (void *)).

    Thread_params* params = (Thread_params *)packed_param;			//use ptr to make forced transform(coercion) flixable.
    SSL *ssl = (SSL*)(params->ssl);
    printf("params->ssl OK!\n");
    //sem_t message_sem = params->semaphore;
    printf("params->semaphore OK!\n");
    int ssl_sock_fd = params->client_socket;
    //struct sockaddr_in *ptr_client_addr = (struct sockaddr_in *)(params->ptr_client_addr);
    printf("ptr_client_addr OK!\n");
    //struct sockaddr_in client_addr;
    //client_addr = *ptr_client_addr;
    printf("client_addr OK!\n");
    int pipe_read = params->signal_pipe[0];
    int pipe_write = params->signal_pipe[1];
    printf("signal_pipe_fd OK!\n");
    char client_IP[INET_ADDRSTRLEN];						//"INET_ADDRSTRLEN" define as 32bit ipv4 max volume, it is 16 Bytes. 
    
    
    printf("init OK!\n");
    
    char log_set_pass[40];
    char log_set_name[40];
    char input_username[20];
    char input_password[20];
        
	
    while(true){
    memset(input_username, 0, sizeof(input_username));
    memset(input_password, 0, sizeof(input_password));
    memset(log_set_name, 0, sizeof(log_set_name));
    memset(log_set_pass, 0, sizeof(log_set_pass));
    
    printf("loop enter OK!\n");
    SSL_read(ssl, log_set_name, sizeof(log_set_name));
    printf("connect aim OK!\n");
    
    	int ret = strcmp("login", log_set_name);
	if(ret == 0){
		SSL_write(ssl, "login_verify_start", strlen("login_verify_start"));
		printf("login_verify_start sent!\n");
		break;
	}else if(log_set_name[0] == '3'){
		char user_message[1024];
		printf("user online\n");
		memset(user_message, 0, sizeof(user_message));
		printf("start recv info\n");
		
		key_t key = ftok(".../", 2015);
    		int shmid = shmget(key, 1024, IPC_CREAT | 0666);    
    		if(shmid == -1){
    			perror("sh_user_info_get error.\n");
    			exit(1);
    		}
    
    		User_info* shm_addr = shmat(shmid, NULL, 0);
    		if(shm_addr < 0){
    			perror("sh_user_info_at error.\n");
    			exit(1);
    		}
    		int current_user_id = ++shm_addr[0].user_id;
		
		int epoll_fd = epoll_create1(0);
		struct epoll_event events[MAX_EVENTS];

		struct epoll_event event;
		event.events = EPOLLIN;
		
		SSL_set_fd(ssl, ssl_sock_fd);
		event.data.fd = ssl_sock_fd; 
		epoll_ctl(epoll_fd, EPOLL_CTL_ADD, ssl_sock_fd, &event);
		
		event.data.fd = pipe_read;
		epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pipe_read, &event);
		
		printf("epoll init complelet.\n");
		
		char pipe_buffer[1024];
		int if_already_triggered_fd = 0;
		while (1) {
		printf("User:%d'epoll start waiting.\n", current_user_id);			
    			int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
    			
    			if (nfds == -1) {
        			perror("epoll_wait");
        			break;
    			}
    			printf("epoll triggered.\n");

    			for (int i = 0; i < nfds; i++) {
        			if (events[i].data.fd == ssl_sock_fd) {
        			memset(user_message, 0, sizeof(user_message));

            			printf("ssl_sock_fd triggered.\n");
            			SSL_read(ssl, user_message, sizeof(user_message));
            			

            			user_message[ret] == '\0';
				printf("%s\n", user_message);
				printf("User:%d'epoll triggered,ssl.\n", current_user_id);
				
				strcpy(shm_addr[0].message, user_message);
				//sem_post(&message_sem);
				//printf("sem posted.\n");
			
				//close(pipe_read);
				write(pipe_write, "pipe writed.", strlen("pipe writed."));
				printf("pipe writed.\n");
				memset(user_message, 0, sizeof(user_message));
				
				if_already_triggered_fd = 1;
				usleep(100);
				
        			}
        			
        			else if(events[i].data.fd == pipe_read) {
            			printf("start waiting sem.\n");
				read(pipe_read, pipe_buffer, sizeof(pipe_buffer));
				printf("pipe readed.");
				SSL_write(ssl, shm_addr[0].message, strlen(shm_addr[0].message));
				printf("%d'message send to target already.\n", current_user_id);
				
				static int shm_message_size = 128;
				memset(shm_addr[0].message, 0, shm_message_size);
        			}
    			}
		}
		
		}else if(log_set_name[0] == '4'){
		key_t key = ftok(".../", 2015);
    		int shmid = shmget(key, 1024, IPC_CREAT | 0666);    
    		if(shmid == -1){
    			perror("sh_user_info_get error.\n");
    			exit(1);
    		}
    
    		User_info* shm_addr = shmat(shmid, NULL, 0);
    		if(shm_addr < 0){
    			perror("sh_user_info_at error.\n");
    			exit(1);
    		}
    		//shm_addr[0].ssl = ssl;
    		strcpy(shm_addr[0].user_IP, "192.168.52.128");
    		shm_addr[0].user_id = 1;
	
		printf("target connceted.\n");
		
		SSL_write(ssl, "try send nihao.\n", strlen("try send nihao.\n"));
		printf("try send nihao already.\n");
		
		
		
		int epoll_fd = epoll_create1(0);
		struct epoll_event events[MAX_EVENTS];

		struct epoll_event event;
		event.events = EPOLLIN;
		
		SSL_set_fd(ssl, ssl_sock_fd);
		event.data.fd = ssl_sock_fd; 
		epoll_ctl(epoll_fd, EPOLL_CTL_ADD, ssl_sock_fd, &event);
		
		event.data.fd = pipe_read;
		epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pipe_read, &event);
		
		printf("epoll init complelet.\n");
		
		char buffer[1024];
		while (1) {
    			int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
    			if (nfds == -1) {
        			perror("epoll_wait");
        			break;
    			}
    			printf("epoll triggered.\n");

    			for (int i = 0; i < nfds; i++) {
        			if (events[i].data.fd == ssl_sock_fd) {
        			memset(log_set_name, 0, sizeof(log_set_name));

            			printf("ssl_sock_fd triggered.\n");
            			SSL_read(ssl, log_set_name, sizeof(log_set_name));
            			

            			log_set_name[ret] == '\0';
				printf("%s\n", log_set_name);
				printf("epoll triggered,not ssl.\n");
        			} else if(events[i].data.fd == pipe_read) {

            			printf("start waiting pipe signal.\n");
				read(pipe_read, buffer, sizeof(buffer));
				SSL_write(ssl, shm_addr[0].message, strlen(shm_addr[0].message));
				printf("message send to target already.\n");
        			}
    			}
		}
	}else{		
		while(1){
		printf("register loop enter OK!\n");
		SSL_write(ssl, "1_register_varify_start", strlen("1_register_varify_start"));
		printf("register_varify_start sent!\n");
			memset(log_set_name, 0, sizeof(log_set_name));
			ret = SSL_read(ssl, log_set_name, sizeof(log_set_name));
			log_set_name[ret] = '\0';
			if(strlen(log_set_name) > 10){
				memset(log_set_name, 0, sizeof(log_set_name));

				sprintf(log_set_name, "2_Name length out of max size!");

				SSL_write(ssl, log_set_name,strlen(log_set_name));
				memset(log_set_name, 0, sizeof(log_set_name));
				printf("name lenth out of 10\n");
			}else{
				SSL_write(ssl, "Name set okay_name.", sizeof("3_Name set okay_name."));
				printf("name set okay!");
				break;
			}
			
		}
		
		printf("loop quited.\n");
		
		const char test[] = "this is test.";
		char test_output[256];
		sha256(test, test_output);
		printf("sha256test:%s\n", test_output);
		
		unsigned char username_to_encrypto[256];
		unsigned char encrypted_username[SHA256_DIGEST_LENGTH];
		strcpy(username_to_encrypto, log_set_name);
		sha256(username_to_encrypto, encrypted_username);
		
		//printf("sha256username:%s\n", encrypted_username);
		
		//printf("?sha256username:%s---\n", encrypted_username);
		
		//usleep(1000);
		fflush(stdout);
		
		sha256(test, test_output);
		//printf("sha256test:%s\n", test_output);
		
		char* sql = "INSERT INTO userinfo (username) VALUES (?);";
		rc = sqlite3_prepare_v2(db, sql, -1, &res, NULL);
		if(rc != SQLITE_OK){
			fprintf(stderr, "cannot prepare SQL statement: %s\n",sqlite3_errmsg(db));
			return NULL;
		}
		sqlite3_bind_text(res, 1, log_set_name, -1, SQLITE_STATIC);

		rc = sqlite3_step(res);
		if(rc != SQLITE_DONE){
			fprintf(stderr, "cannot insert data: %s\n",sqlite3_errmsg(db));
			return NULL;
		}

		sqlite3_reset(res);
		//printf("sha256username_steps sql:%s\n", encrypted_username);
		

		while(1){
			ret = SSL_read(ssl, log_set_pass, sizeof(log_set_pass));
			printf("sha256username_recved pass:%s\n", encrypted_username);
			log_set_pass[ret] = '\0';
			if(strlen(log_set_pass) > 10){
				memset(log_set_pass, 0, sizeof(log_set_pass));
				sprintf(log_set_pass, "4_Password length out of max size!");
				SSL_write(ssl, log_set_pass,strlen(log_set_pass));
				memset(log_set_pass, 0, sizeof(log_set_pass));
				
			}else{
				SSL_write(ssl, "5_Password set okay_pass", sizeof("5_Password set okay_pass"));
				printf("Password set okay sent!\n");
				break;
			}
			
		}
		printf("sha256username_before set pass:%s\n", encrypted_username);
		
		unsigned char pass_to_encrypto[256];
		unsigned char encrypted_pass[SHA256_DIGEST_LENGTH];
		strcpy(pass_to_encrypto, log_set_pass);
		sha256(pass_to_encrypto, encrypted_pass);
		
		printf("sha256username_encrypted pass:%s\n", encrypted_username);

		sql = "UPDATE userinfo SET password = ? WHERE username = ?;";
		rc = sqlite3_prepare_v2(db, sql, -1, &res, NULL);												
		if(rc != SQLITE_OK){
			fprintf(stderr, "cannot prepare SQL statement: %s\n",sqlite3_errmsg(db));
		 	return NULL;
		}	        			
		sqlite3_bind_text(res, 1, log_set_pass, -1, SQLITE_STATIC);
		sqlite3_bind_text(res, 2, log_set_name, -1, SQLITE_STATIC);
		
		printf("2sha256username:%s---\n", encrypted_username);
		
		printf("2sha256password:%s---\n", encrypted_pass);
		fflush(stdout);

		rc = sqlite3_step(res);
		if(rc != SQLITE_DONE){
			fprintf(stderr, "cannot insert data: %s\n",sqlite3_errmsg(db));
			return NULL;
		}
		
		printf("3sha256username:%s---\n", encrypted_username);
		
		printf("3sha256password:%s---\n", encrypted_pass);
		SSL_write(ssl, "6_Request OK!", strlen("6_Request OK!"));
		printf("allset sent!");
		insert_user(log_set_name,log_set_pass);
		memset(log_set_name, 0, sizeof(log_set_name));
		memset(log_set_pass, 0, sizeof(log_set_pass));
		
		memset(username_to_encrypto, 0, sizeof(username_to_encrypto));
		memset(encrypted_username, 0, sizeof(encrypted_username));
		memset(pass_to_encrypto, 0, sizeof(pass_to_encrypto));
		memset(encrypted_pass, 0, sizeof(encrypted_pass));
		
		sqlite3_reset(res);
		break;
	}
    }
    while(true){
    char encrypted_input_username[256];
    char encrypted_input_pass[256];
    
    	int username_len = SSL_read(ssl, input_username, sizeof(input_username));
	if(username_len < 0) pthread_exit(NULL);
	else if(username_len > 0){
		input_username[username_len] = '\0';			
		
		
		sha256(input_username, encrypted_input_username);

		//write(STDOUT_FILENO, input_username, username_len);
		//SSL_write(ssl, input_username, sizeof(input_username));
	}
	
	SSL_write(ssl, "username_input_done", strlen("username_input_done"));
	printf("username_input_done sent!\n");
	
	
	int password_len = SSL_read(ssl, input_password, sizeof(input_password));
	if(password_len < 0) pthread_exit(NULL);
	else if(username_len > 0){
		input_password[password_len] = '\0';
		
		
		sha256(input_password, encrypted_input_pass);
		
		printf("encrypted pass:%s---",encrypted_input_pass);
		fflush(stdout);
					
		//write(STDOUT_FILENO, input_password, password_len);
		//SSL_write(ssl, input_password, sizeof(input_password));
	}
	
	SSL_write(ssl, "password_input_done", strlen("password_input_done"));
	printf("password_input_done sent!\n");

	//printf("input_username is:%s\n", input_username);
	//printf("input_password is:%s\n", input_password);
	//printf("input_username's hash is :%d\n", hash_func(input_username));

	int ret = is_user_permited(input_username, input_password);
	int allset = 0;
	switch(ret){
		case 0:allset = 1;break;
		case 1:SSL_write(ssl, "1\033[0;34mConnetion refused!\033[0m\nReason:Unauthorized user.\n", sizeof("1\033[0;34mConnetion refused!\033[0m\nReason:Unauthorized user.\n"));break;
		case 2:SSL_write(ssl, "1\033[0;34mConnetion refused!\033[0m\nReason:Incorrect password.\n", sizeof("1\033[0;34mConnetion refused!\033[0m\nReason:Incorrect password.\n"));break;
		case 3:SSL_write(ssl, "1\033[0;34mConnetion refused!\033[0m\nReason:Unknow error.\n", sizeof("1\033[0;34mConnetion refused!\033[0m\nReason:Unknow error.\n"));break;
	}
	if(allset){
	printf("allset\n");	    	
	    break;
	}else{
	printf("incorrct username or password.\n");
	    SSL_write(ssl, "incorrct username or password.", sizeof("incorrct username or password."));break;
	}
        memset(input_username, 0, sizeof(input_username));
        memset(input_password, 0, sizeof(input_password));
        memset(encrypted_input_pass, 0, sizeof(encrypted_input_pass));
        memset(encrypted_input_username, 0, sizeof(encrypted_input_username));
    }

    SSL_write(ssl, "0\033[0;34mConnetion authorized!\033[0m\nPermition level \033[0;31m2\033[0m.\nWelcome back!\n", sizeof("0\033[0;34mConnetion authorized!\033[0m\nPermition level \033[0;31m2\033[0m.\nWelcome back!\n"));

    int client_code_current = ++client_code;

    /*monitor online users and output infomation include IP & fact Port, show online popularity as well.*/
    //printf("\n\033[0;31mNew user online!\033[0m\n==================\nUser detail below:\nIP:\033[0;36m\033[0m\nPort:\033[0;32m\033[0m\nUsername:\033[0;36m%s\033[0m\n==================\ncurrent user: \033[0;34m%d\033[0m\n---------------\n\n",
    //		    input_username, (++online_client));
    
    char buffer[1024];
    memset(buffer, 0, sizeof(buffer));
    while(1){
    	int len = SSL_read(ssl, buffer, sizeof(buffer)-1);
	    if (len < 0){
	        //printf("SSL read error.\n");
		int error = SSL_get_error(ssl, len);
		if(error == SSL_ERROR_ZERO_RETURN)
		    printf("SSL_ERROR_ZERO_RETURN.\n");				//cause by client shutdown dircetly.
		else if(error == SSL_ERROR_SYSCALL){
		//  printf("SSL_ERROR_SYSCALL.\n");					//basic I/O ERROR.May client unexpected shutdown.
		//  SSL_shutdown(ssl);							//THIS FUNC MAKE SERVER COLLAPSE.
	    	SSL_free(ssl);
	        }
                break;
	    }else if(len == 0){
    	        SSL_shutdown(ssl);
		SSL_free(ssl);
    	        pthread_exit(NULL);	    	
	    }
	    else if(len > 0){
	        write(STDOUT_FILENO, "User ", strlen("User "));
		//char *ptr = (char *)client_code;					//can not use ptr,reason followed.
		char client_code_arr[30];
		sprintf(client_code_arr, "\033[0;31m%d \033[0m:", client_code_current);	 //THERE IS A BUFFER SPOILL PROBLEM.
	        write(STDOUT_FILENO, client_code_arr, strlen(client_code_arr)); //ptr do not include '\0',when use strlen(char *ptr) will throw exception"zsh: segmentation fault",use arr to strlen makesure there is an '\0'.
	        write(STDOUT_FILENO, buffer, len);
		SSL_write(ssl, buffer, sizeof(buffer));
	    }
    }
    	    printf("User disconnected,current user: \033[0;31m%d\n\033[0m",(--online_client));
    	    pthread_exit(NULL);
}

int send_to_target(char *target_ip_recv, char *user_message){
	struct sockaddr_in target_addr;
	int target_socket = socket(AF_INET, SOCK_STREAM, 0);
    	target_addr.sin_family = AF_INET;
    	target_addr.sin_port = htons(SERVER_PORT);
    	inet_pton(AF_INET, target_ip_recv, &target_addr.sin_addr);
        
        SSL *ssl_target = sync_initialize_ssl(target_socket);
        		
        SSL_write(ssl_target, user_message, sizeof(user_message));
        
        return 1;
}


/*
 * @description   	    : start sqlite service and init user info
 * @return        	    : if init success 
 */
int8_t init_sqlite_database(){

    rc =sqlite3_open("database_for_socket", &db);
	if(rc != SQLITE_OK){
		fprintf(stderr,"cannot open database: %s\n",sqlite3_errmsg(db));
		sqlite3_close(db);
		return 1;
	}

	const char *sql = "SELECT username, password FROM userinfo;";
	rc = sqlite3_prepare_v2(db, sql, -1, &res, 0);

	if(rc != SQLITE_OK){
		fprintf(stderr, "cannot execute statement : %s\n", sqlite3_errmsg(db));
		return -1;
	}

	while(sqlite3_step(res) == SQLITE_ROW){
		const char *username = (const char *)sqlite3_column_text(res, 0);
		const char *password = (const char *)sqlite3_column_text(res, 1);
		insert_user(username, password);
	}
    printf("Sqlite3 database data trans to local server compeleted. \nAdministrator,\033[0;31mwelcome back!\033[0m\n");
}


/*
 * @description   	      : set params for func handle client'callback
 * @param - client_addr   : client address structure
 * @return        	      : no return 
 */
Thread_params set_thread_param(struct sockaddr_in client_addr, SSL *ssl){
	Thread_params thread_params = {NULL,NULL};
	thread_params.ptr_client_addr = (void *)&client_addr;
	thread_params.ssl = (void *)ssl;
	//thread_params.client_socket_ptr = client_socket_ptr;	
	
}

/*
 * @description   	      : initalize server's listen socket
 * @return        	      : server socket 
 */
int8_t init_server_socket(){

    int server_socket = socket(AF_INET, SOCK_STREAM, 0);
    
    if (server_socket < 0) {
        printf("Create server socket error.\n");
        return -1;
    }
    
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(SERVER_PORT);
    
    if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        printf("Error binding socket\n");
        return -1;
    }
    
    if (listen(server_socket, 5) < 0) {
        printf("Error listening on socket\n");
        return -1;
    }
    return server_socket;
}

/*
 * @description   	       	    : initalize client's communicate socket
 * @param - server_socket       : server's socket 
 * @param - client_addr         : client address structure 
 * @param - client_addr_len     : size of client address structure
 * @return        	            : client socket
 */
int8_t init_client_socket(int server_socket, struct sockaddr_in client_addr,socklen_t client_addr_len){

    

    int client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &client_addr_len);
        if (client_socket < 0) {
            printf("Error accepting client connection\n");
            return -1;
        }   
        
        return client_socket;
}

/*
 * @description   	       	    : finalize and free socket and database
 * @param - server_socket       : server's socket 
 * @return        	            : no return
 */
void finalize_all(int server_socket){

    sqlite3_finalize(res);
    sqlite3_close(db);

    close(server_socket);
    SSL_CTX_free(ctx);									
}

/*
 * @description   	       	    : create thread and record tid
 * @param - thread_params       : param pack of callback func
 * @param - thread_counter      : thread num, to record position of current in threads[]
 * @param - threads             : array that record all threads' tid
 * @param - tid                 : thread's identification
 * @return        	            : if successed
 */
int8_t thread_ops(Thread_params thread_params, int thread_counter, pthread_t *threads){
    pthread_t tid; 									
    threads[thread_counter++] = tid;
    
    pthread_create(&tid, NULL, handle_client, (void *)&thread_params);		
    
    pthread_detach(tid);								
    
    return 0;
}

/*
 * @description   	       	    : stop server service
 * @param - thread_params       : server's listen socket
 * @return        	            : no return
 */
void disconnect_server(int server_socket){

	printf("main thread exit, server shutdown!");
	
	finalize_all(server_socket);
}

int8_t main(){
    
    sem_init(&message_sem, 0, 0);

    key_t key = ftok(".../", 2015);
    int shmid = shmget(key, 1024, IPC_CREAT | 0666);    
    if(shmid == -1){
    	perror("sh_user_info_get error.\n");
    	exit(1);
    }
    
    User_info* shm_addr = shmat(shmid, NULL, 0);
    if(shm_addr < 0){
    	perror("sh_user_info_at error.\n");
    	exit(1);
    }
    
    printf("write shm ing.");
    shm_addr[0].user_id = 0;
    printf("write shm compelet.");
    
    
    
    system("ipcs -m");
    
    int pipefd[2];
    if(pipe(pipefd) == -1){
    	perror("pipe");
    	return -1;
    }
    
    init_sqlite_database();
    
    int server_socket = init_server_socket();
    
    struct sockaddr_in client_addr;
    socklen_t client_addr_len = sizeof(client_addr);
    
    int thread_counter = 0;

    pthread_t threads[MAX_THREADS_NUM];
    
    modbus_t *ctx = NULL;
    modbus_mapping_t *mb_mapping = NULL;
    int rc = 0;
    
    pthread_t modbus_threadID;
    threads[thread_counter++] = modbus_threadID;
    
    pthread_create(&modbus_threadID, NULL, modbus_server, NULL);
    
    pthread_detach(modbus_threadID);
    
      	  
    while (1) {
                   
        int client_socket = init_client_socket(server_socket, client_addr, client_addr_len); 
        if(client_socket < 0) continue;
        
        SSL *ssl = sync_initialize_ssl(client_socket);				
        
        void *client_socket_ptr = (void *)&client_socket;
        //Thread_params thread_params = set_thread_param(client_addr, ssl);
        Thread_params thread_params = {NULL, NULL, 0, 0};
	thread_params.ptr_client_addr = (void *)&client_addr;
	thread_params.ssl = (void *)ssl;
	thread_params.client_socket = client_socket;
	thread_params.semaphore = message_sem;
	memcpy(thread_params.signal_pipe, pipefd, sizeof(pipefd));
	
                      
        //thread_ops(thread_params, thread_counter, threads);
        pthread_t tid; 									
    	threads[thread_counter++] = tid;
    
    	pthread_create(&tid, NULL, handle_client, (void *)&thread_params);		
    
    	pthread_detach(tid);	       
        						         	
    }

    disconnect_server(server_socket);
    return 0;   
}


